package com.liesp.base;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

import com.liesp.util.BeanUtil;
import com.liesp.util.ListUtil;
import com.liesp.util.PropertyFilter;
import com.liesp.util.ReflectionUtils;
import com.liesp.util.StringUtil;
import com.liesp.util.PropertyFilter.MatchType;

/**
 * 项目名称：Liesp<br>
 * 类名称：BaseDAO <br>
 * 类描述： 所有DAOImpl的基类,提供各DAOImpl的通用功能 <br>
 * 创建人：许彪 创建时间：2009-8-28 上午11:20:02 <br>
 * 修改人： 修改时间： <br>
 * 修改备注：
 * 
 * @version V1.0
 */
public class BaseDAO<T, PK extends Serializable> extends HibernateDaoSupport {

	private static org.hibernate.cfg.Configuration con;

	private static Logger logbase = LoggerFactory.getLogger(BaseDAO.class);

	protected Class<T> entityClass;
	protected SessionFactory sessionFactory;

	/**
	 * 用于Dao层子类使用的构造函数. 通过子类的泛型定义取得对象类型Class. eg. public class UserDao extends
	 * SimpleHibernateDao<User, Long>
	 */
	public BaseDAO() {
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
	}

	/**
	 * 根据hql语句及对应的参数统计符合条件的数量
	 * 
	 * @param hql
	 * @param values
	 * @return
	 */
	public long countByHql(String hql, Object[] params) {
		long r = 0;
		Session session = null;
		try {
			logger.debug(hql);
			session = this.getSession();
			int length = params == null ? 0 : params.length;
			Query query = session.createQuery(hql);
			for (int i = 0; i < length; i++) {
				query.setParameter(i, params[i]);
			}
			r = ((Number) query.iterate().next()).longValue();

		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			session.close();
		}
		return r;
	}

	public long countBySql(String sql, Object[] params) {
		long r = 0;
		Session session = this.getSession();
		SQLQuery query = session.createSQLQuery(sql);
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		}
		r = ((Number) query.iterate().next()).longValue();

		return r;
	}

	/**
	 * 批量删除对象
	 * 
	 * @param ids
	 * @return
	 * @author wxy 2010-03-29
	 */
	public int delete(final List keys, String keyName) {
		int r = 0;
		if (keys == null || keys.size() < 1) {
			return r;
		}
		StringBuilder hqlDel = new StringBuilder(100);
		hqlDel.append("delete from ").append(this.getEntityName());
		int length = keys.size();
		for (int i = 0; i < length; i++) {
			if (i == 0) {
				hqlDel.append(" where ");
			}
			hqlDel.append(" ").append(keyName).append("=  ? ");
			if (i != length - 1) {
				hqlDel.append(" or ");
			}
		}
		r = this.getHibernateTemplate().bulkUpdate(hqlDel.toString(),
				keys.toArray());
		return r;
	}

	/**
	 * 删除数据库记录 慎用
	 * 
	 * @param obj
	 *            对象
	 * @return 成功返回1
	 */
	public Long delete(Object obj) {
		this.getHibernateTemplate().delete(obj);
		return 1L;
	}

	/**
	 * 通过主键id删除数据库记录
	 * 
	 * @param claz
	 * @param id
	 * @return
	 */
	public Long delete(Class claz, java.io.Serializable id) {
		Session session = this.getSession();
		Transaction tx = null;// 事务
		try {
			tx = session.beginTransaction();
			this.delete(session.get(claz, id));
			tx.commit();
		} catch (HibernateException he) {
			he.printStackTrace();
			if (tx != null) {
				tx.rollback();// 回滚
			}
		}
		return 1L;
	}

	/**
	 * 根据HQL进行查询
	 * 
	 * @param hql
	 * @return
	 */
	public List<T> findByHql(String hql) {
		return this.findByHql(hql, null);
	}

	/**
	 * 通过HQL语句查询,用于不分页的情况
	 * 
	 * @param hql
	 *            查询语句，变量以'?'代替
	 * @param obj
	 *            变量数组，于hql中的'?'相对应
	 * @return 查询结果List
	 */
	public List<T> findByHql(String hql, Object[] obj) {
		List<T> list = this.findByHql(hql, obj, 1, 0);
		return list == null ? new ArrayList<T>() : list;
	}

	/**
	 * 通过HQL语句分页查询
	 * 
	 * @param hql
	 *            查询语句，变量以'?'代替
	 * @param obj
	 *            变量数组，于hql中的'?'相对应
	 * @param pageIndex
	 *            开始页数
	 * @param perPage
	 *            每页条数
	 * @return
	 */
	public List<T> findByHql(String hql, Object[] obj, int pageIndex,
			int perPage) {
		Session session = this.getSession();
		List<T> list = new ArrayList<T>();

		Query query = session.createQuery(hql);
		if (obj != null && obj.length > 0) {
			for (int i = 0; i < obj.length; i++) {
				query.setParameter(i, obj[i]);
			}
		}
		if (pageIndex > 0 && perPage > 0) {
			query.setFirstResult((pageIndex - 1) * perPage);
			query.setMaxResults(perPage);
		}
		list = query.list();

		return list == null ? new ArrayList<T>() : list;
	}

	/**
	 * <p>
	 * 根据主码查询对象
	 * </p>
	 * 
	 * @param id
	 *            主键
	 * @return 类对象
	 * @author wxy 2010-03-29
	 * @return
	 */
	public Object findById(Serializable id) {
		return getHibernateTemplate().get(this.getEntityName(), id);
	}

	/**
	 * 根据keys列表查找数据,根据传入的参数已经排序
	 * 
	 * @param keys
	 *            需要查询的主码列表值
	 * @param keyName
	 *            主键名称
	 * @return 已排序的结果集
	 * @author wxy 2010-03-29
	 */
	public List<T> findByKeys(Serializable[] keys, String keyFieldName) {
		// 对keys进行排重
		Serializable[] onlyKeys = BeanUtil.getOnly(keys);
		int length = onlyKeys == null ? 0 : onlyKeys.length;
		if (length <= 0) {
			return new ArrayList(0);
		}
		List<T> r = findByKeysFromDB(onlyKeys, keyFieldName);// 从数据库查询未排序的值
		if (ListUtil.isNullOrEmpty(r)) {// 如果返回的list为空则自动返回
			return r;
		}
		// 重新排序
		HashMap<Object, T> hmap = new HashMap<Object, T>(length);
		try {
			Method m = null;
			m = r.get(0).getClass().getMethod(
					"get" + StringUtil.setFirstCharUpcase(keyFieldName),
					new Class[0]);
			for (T o : r) {
				hmap.put(m.invoke(o, new Object[0]), o);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (r != null) {
			r.clear();
		}
		T value = null;
		try {
			for (int i = 0; i < keys.length; i++) {// 如果没有就返回默认值
				value = hmap.get(keys[i]);
				value = value == null ? this.getEntityClass().newInstance()
						: value;
				r.add(value);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		hmap.clear();
		return r;
	}

	/**
	 * 从数据库分配查询对应key的值，未排序
	 * 
	 * @param keys
	 * @param keyFieldName
	 * @return
	 */
	protected List<T> findByKeysFromDB(Serializable[] keys, String keyFieldName) {
		if (keys == null || keys.length < 1) {
			return new ArrayList<T>();
		}
		List<Serializable> keyList = Arrays.asList(keys);

		ArrayList<T> datas = new ArrayList<T>(keys.length);
		Session session = null;
		try {
			StringBuilder hqlBuilder = new StringBuilder(100);
			hqlBuilder.append(" from ").append(this.getEntityName()).append(
					" o where ").append(keyFieldName).append(" in( :ID )");
			// 将uniqueKeys分成几个list
			List<List<Serializable>> subList = ListUtil.splitList(keyList, 100);
			// 查询
			session = this.getSession();
			Query query = session.createQuery(hqlBuilder.toString());
			for (List<Serializable> list : subList) {
				query.setParameterList("ID", list);
				List<T> values = (List<T>) query.list();
				datas.addAll(values);
			}
		} finally {
			session.close();
		}
		return datas;
	}

	public List<T> findBySql(String sql) {
		return this.findBySql(sql, null);
	}

	public List<T> findBySql(String sql, Object[] params) {
		return this.findBySql(sql, params, 1, 0);
	}

	public List<T> findBySql(String sql, Object[] params, int pageIndex,
			int rowsPerPage) {
		List<T> param = new ArrayList<T>();
		Session session = this.getSession();
		org.hibernate.Transaction tx = session.beginTransaction();
		try {
			SQLQuery query = session.createSQLQuery(sql);
			if (params != null && params.length > 0) {
				for (int i = 0; i < params.length; i++) {
					query.setParameter(i, params[i]);
				}
			}
			if (pageIndex > 0) {
				query.setFirstResult((pageIndex - 1) * rowsPerPage);
				query.setMaxResults(rowsPerPage);
			}
			param = query.list();
			tx.commit();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			session.close();
		}
		return param;
	}

	/**
	 * 根据sql语句查询，返回List<T>
	 * 
	 * @param sql
	 *            sql语句
	 * @return List<T>
	 */
	public List<T> findEntityBySql(String sql) {
		return this.findEntityBySql(sql, null);
	}

	/**
	 * 根据sql语句查询，返回List<T>
	 * 
	 * @param sql
	 *            sql语句
	 * @param params
	 *            参数列表
	 * @return List<T>
	 */
	public List<T> findEntityBySql(String sql, Object[] params) {
		return this.findEntityBySql(sql, params, 1, 0);
	}

	/**
	 * 根据sql语句查询，返回List<T>
	 * 
	 * @param sql
	 *            sql语句
	 * @param params
	 *            参数列表
	 * @param pageIndex
	 *            页码（从1开始）
	 * @param rowsPerPage
	 *            每页记录数
	 * @return List<T>
	 */
	public List<T> findEntityBySql(String sql, Object[] params, int pageIndex,
			int rowsPerPage) {
		List<T> result = new ArrayList<T>();
		Session session = this.getSession();

		SQLQuery query = session.createSQLQuery(sql).addEntity(
				this.getEntityName());
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		}
		if (pageIndex > 0) {
			query.setFirstResult((pageIndex - 1) * rowsPerPage);
			query.setMaxResults(rowsPerPage);
		}
		result = query.list();

		return result;
	}

	/**
	 * 根据主键的内容，创建in查询的括号内问号部分的字符串
	 * 
	 * @param keys
	 * @return 字符串
	 */
	public String buildKeyCondition(Serializable[] keys) {
		if (keys == null || keys.length <= 0) {
			return "";
		}
		StringBuilder strBuilder = new StringBuilder(keys.length * 3);
		for (int i = 0; i < keys.length; i++) {
			if (i != 0) {
				strBuilder.append(",");
			}
			strBuilder.append("?");
		}
		return strBuilder.toString();
	}

	protected Class<T> getEntityClass() {
		Class<T> r = null;
		try {
			r = (Class<T>) Class.forName(this.getEntityName());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return r;
	}

	protected String getEntityName() {
		String name = this.getClass().getGenericSuperclass().toString();
		if (name != null && name.indexOf('<') > 0) {
			name = name.substring(name.indexOf('<') + 1, name.indexOf('>'));
		} else if (name.indexOf("HibernateEntityController") == -1) {
			name = null;
			logbase.warn("{} is a subclass BaseDAO,you must extend "
					+ "BaseDAO<T> ,T is required!", this.getClass().getName());
		}
		return name;
	}

	/**
	 * 更新
	 * 
	 * @param obj为更新对象
	 * @return
	 */
	public T merge(T obj) {
		obj = (T) this.getHibernateTemplate().merge(obj);
		return obj;
	}

	/**
	 * 保存
	 * 
	 * @param obj为保存对象
	 * @return
	 */
	public Serializable save(Object obj) {
		Serializable id = (Serializable) this.getHibernateTemplate().save(obj);
		return id;
	}

	public int updateByHql(String hql, Object[] obj) {
		return this.getHibernateTemplate().bulkUpdate(hql, obj);
	}

	/**
	 * 用原生的SQL语句 update操作
	 * 
	 * @param sql
	 * @return
	 */
	public int updateBySql(String sql) {
		int r = 0;
		Session session = this.getSession();
		r = session.createSQLQuery(sql).executeUpdate();
		return r;
	}

	/**
	 * 批量修改
	 * 
	 * @param colName
	 * @param value
	 * @param keys
	 * @return
	 */
	public int updateColumByHql4Key(String colName, Object value,
			final List keys, String className, String keyName) {
		int r = 0;
		if (keys == null || keys.size() < 1)
			return r;
		String hqlDel = "update " + className + " set " + colName + "= ?";
		int length = keys.size();
		for (int i = 0; i < length; i++) {
			if (i == 0)
				hqlDel += " where ";
			hqlDel = hqlDel + " " + keyName + "=  ? ";
			if (i != length - 1)
				hqlDel += " or ";
		}
		Object[] values = new Object[length + 1];
		values[0] = value;
		System.arraycopy(keys.toArray(), 0, values, 1, length);
		r = this.getHibernateTemplate().bulkUpdate(hqlDel, values);
		return r;
	}

	/**
	 * 根据Criterion条件创建Criteria. 与find()函数可进行更加灵活的操作.
	 * 
	 * @param criterions
	 *            数量可变的Criterion.
	 */
	public Criteria createCriteria(final Criterion... criterions) {
		Criteria criteria = getSession(true).createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	/**
	 * left join fetch 方法
	 * 
	 * @param className
	 * @param criterions
	 * @return
	 */
	public Criteria createCriteriaLeftJoinFetch(final String[] orderName,final String[] orderesc,final String[] className,
			final Criterion... criterions) {
		Criteria criteria = getSession(true).createCriteria(entityClass);
		for (String cname : className) {
			criteria.createAlias(cname, cname);
		}
		int num=0;
		if(orderName.length>0){
			for(String str:orderName){
				if(orderesc.length==0){
					criteria.addOrder(Order.asc(str));
				}else if (orderesc[num].equals("asc")) {
					criteria.addOrder(Order.asc(str));
				}else if (orderesc[num].equals("desc")) {
					criteria.addOrder(Order.desc(str));
				}
				num++;
			}
		}
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	/**
	 * 按属性查找对象列表,支持多种匹配方式.
	 * 
	 * @param matchType
	 *            匹配方式,目前支持的取值见PropertyFilter的MatcheType enum.
	 */
	public List<T> findBy(final String propertyName, final Object value,
			final MatchType matchType) {
		Criterion criterion = buildCriterion(propertyName, value, matchType);
		return find(criterion);
	}

	/**
	 * 按Criteria查询对象列表.
	 * 
	 * @param criterions
	 *            数量可变的Criterion.
	 */
	public List<T> find(final Criterion... criterions) {
		List<T> list = createCriteria(criterions).list();
		return list;
	}

	/**
	 * 按Criteria查询对象列表，left join fetch方法
	 * 
	 * @param claName
	 * @return
	 */
	public List<T> find(final String[] orderName,final String[] orderesc,final String[] claName, final Criterion... criterions) {
		List<T> list = createCriteriaLeftJoinFetch(orderName,orderesc,claName, criterions).list();
		return list;
	}

	/**
	 * 抓取对象方法
	 * 
	 * @param filters
	 * @param clasname
	 * @return
	 */
	public List<T> find(List<PropertyFilter> filters, String[] clasname,String[] orderName,String[] orderesc) {
		Criterion[] criterions = buildCriterionByPropertyFilter(filters);
		List<T> list = find(orderName,orderesc,clasname, criterions);
		return list;
	}

	/**
	 * 按属性过滤条件列表查找对象列表.
	 */
	public List<T> find(List<PropertyFilter> filters) {
		Criterion[] criterions = buildCriterionByPropertyFilter(filters);
		List<T> list = find(criterions);
		return list;
	}

	/**
	 * 按属性条件参数创建Criterion,辅助函数.
	 */
	protected Criterion buildCriterion(final String propertyName,
			final Object propertyValue, final MatchType matchType) {
		Assert.hasText(propertyName, "propertyName不能为空");
		Criterion criterion = null;
		// 根据MatchType构造criterion
		switch (matchType) {
		case EQ:
			criterion = Restrictions.eq(propertyName, propertyValue);
			break;
		case LIKE:
			criterion = Restrictions.like(propertyName, (String) propertyValue,
					MatchMode.ANYWHERE);
			break;

		case LE:
			criterion = Restrictions.le(propertyName, propertyValue);
			break;
		case LT:
			criterion = Restrictions.lt(propertyName, propertyValue);
			break;
		case GE:
			criterion = Restrictions.ge(propertyName, propertyValue);
			break;
		case GT:
			criterion = Restrictions.gt(propertyName, propertyValue);
		}
		return criterion;
	}

	/**
	 * 按属性条件列表创建Criterion数组,辅助函数.
	 */
	protected Criterion[] buildCriterionByPropertyFilter(
			final List<PropertyFilter> filters) {
		List<Criterion> criterionList = new ArrayList<Criterion>();
		for (PropertyFilter filter : filters) {
			if (!filter.hasMultiProperties()) { // 只有一个属性需要比较的情况.
				Criterion criterion = buildCriterion(filter.getPropertyName(),
						filter.getMatchValue(), filter.getMatchType());
				criterionList.add(criterion);
			} else {// 包含多个属性需要比较的情况,进行or处理.
				Disjunction disjunction = Restrictions.disjunction();
				for (String param : filter.getPropertyNames()) {
					Criterion criterion = buildCriterion(param, filter
							.getMatchValue(), filter.getMatchType());
					disjunction.add(criterion);
				}
				criterionList.add(disjunction);
			}
		}
		return criterionList.toArray(new Criterion[criterionList.size()]);
	}

	@Autowired
	public void setSessionFactorysf(SessionFactory sf) {
		this.setSessionFactory(sf);
	}

}
